//
// Created by daybeha on 2022/7/8.
//

//c++ python联合编程 问题记录
//
//        多线程下代码框架完成，
//但Mat转numpy的核心函数 PyArray_SimpleNewFromData（）在第三次调用时会报段错误


#define PY_SSIZE_T_CLEAN

#include <Python.h>
#include <iostream>
#include <opencv2/opencv.hpp>
#include "numpy/arrayobject.h"
#include <stdio.h>
//#include <time.h>
//#include <pthread.h>

#include <thread>
#include "loopclose.h"

using namespace std;


const char *python_modle = "sg_match";
//const char * python_func =  "compute_score";
const char *python_func = "con_show";
//const char * python_func =  "show_img";


PyObject *Mat2Numpy(cv::Mat img) {
    int r = img.rows;
    int c = img.cols;
    int ch = img.channels();
    // total number of elements (here it's an RGB image of size 640x480)
    int nElem = r * c * ch;

    // 创建数组类型，保存上面读到的图片
    // create an array of apropriate datatype
    uchar *m = new uchar[nElem];
    // copy the data from the cv::Mat object into the array
    std::memcpy(m, img.data, nElem * sizeof(uchar));

//    // the dimensions of the matrix
//    npy_intp mdim[] = { r, c, ch};
//    // 将cv::Mat 转为 numpy.array 类型 convert the cv::Mat to numpy.array
//    PyObject* mat = PyArray_SimpleNewFromData(ch, mdim, NPY_UINT8, (void*) m);

    // the dimensions of the matrix
    PyObject *mat;
    cout << "mdim : " << endl;
    if (ch == 1) {
        npy_intp mdim[2] = {r, c};
        cout << mdim[0] << ", " << mdim[1] << ", " << mdim[2] << endl;
        // 将cv::Mat 转为 numpy.array 类型 convert the cv::Mat to numpy.array
        mat = PyArray_SimpleNewFromData(2, mdim, NPY_UINT8, (void *) m);
    } else {
        npy_intp mdim[3] = {r, c, ch};
        cout << mdim[0] << ", " << mdim[1] << ", " << mdim[2] << endl;
        mat = PyArray_SimpleNewFromData(3, mdim, NPY_UINT8, (void *) m);
    }

//    delete[] m;     // 不能删，删了第二张图片会覆盖第一张
    return mat;
}

float compute_match_score(cv::Mat &img0, cv::Mat &img1, PyObject *pFunc) {
    float score = -1;
    PyObject *mat0 = Mat2Numpy(img0);
    PyObject *mat1 = Mat2Numpy(img1);

    PyObject *pArgs = PyTuple_New(2);     // 必须先创建Tuple
    PyTuple_SetItem(pArgs, 0, mat0);
    PyTuple_SetItem(pArgs, 1, mat1);

//        PyObject *pArgs = PyTuple_New(1);     // 必须先创建Tuple
//        PyTuple_SetItem(pArgs, 0, PyLong_FromLong(5));

    PyObject *result = PyEval_CallObject(pFunc, pArgs);

    if (result != NULL) {
        score = PyFloat_AsDouble(result);
        printf("Result of call: %f\n", score);
        Py_DECREF(result);
    } else {
        cerr << "result Null" << endl;
    }

    // 释放引用 decrement the object references
    Py_XDECREF(mat0);
    Py_XDECREF(mat1);
    Py_XDECREF(result);
    Py_XDECREF(pArgs);

    return score;
}

void loadPython() {
    PyObject *pModule, *pFunc;
    // 初始化
    Py_Initialize();

    PyRun_SimpleString("import sys\n");
    PyObject *sys_path = PySys_GetObject("path");
    PyList_Append(sys_path, PyUnicode_FromString("/home/daybeha/Documents/github/DeepLabV3_ws/src/superglue/script"));
    PyRun_SimpleString("print(sys.path)");

    // 检查当前Python版本
    PyRun_SimpleString("import platform\n"
                       "print(f\"\\033[92m Python Version: {platform.python_version()}\\033[0m\")\n");

    // 在C中使用numpy之前一定要先 import_array()
    // https://numpy.org/doc/1.13/reference/c-api.array.html#c.import_array
//    import_array1(-1);
    _import_array();

    // 导入 python 模块
    pModule = PyImport_ImportModule(python_modle);

    if (pModule != NULL) {
        cout << "loaded " << python_modle << endl;

        // 获取目标python函数
        pFunc = PyObject_GetAttrString(pModule, python_func);

        /* pFunc is a new reference */
        if (pFunc && PyCallable_Check(pFunc)) {
            // 读取图片
            cv::Mat img0 = cv::imread("/home/daybeha/Documents/Dataset/Kitti/sequences/00/image_0/000000.png",
                                      cv::IMREAD_GRAYSCALE);
            cv::Mat img1 = cv::imread("/home/daybeha/Documents/Dataset/Kitti/sequences/00/image_0/000020.png",
                                      cv::IMREAD_GRAYSCALE);

            float score = compute_match_score(img0, img1, pFunc);
            cout << "score: " << score << endl;
        } else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function \"%s\"\n", python_func);
        }
        Py_XDECREF(pFunc);
        Py_DECREF(pModule);
    } else {
        PyErr_Print();
        fprintf(stderr, "Failed to load \"%s\"\n", python_modle);

        return;
    }

    Py_FinalizeEx();
}


/**
 * @brief 图像拼接
 * @param[in] images    原始图像
 * @param[in] type      0：横向拼接； 1： 纵向拼接
 * */
cv::Mat ImageSplicing(vector<cv::Mat> images, int type) {
    if (type != 0 && type != 1)
        type = 0;

    int num = images.size();
    int newrow = 0;
    int newcol = 0;
    cv::Mat result;

    // 横向拼接
    if (type == 0) {
        int minrow = 10000;
        for (int i = 0; i < num; ++i) {
            if (minrow > images[i].rows)
                minrow = images[i].rows;
        }
        newrow = minrow;
        for (int i = 0; i < num; ++i) {
            int tcol = images[i].cols * minrow / images[i].rows;
            int trow = newrow;
            cv::resize(images[i], images[i], cv::Size(tcol, trow));
            newcol += images[i].cols;
            if (images[i].type() != images[0].type())
                images[i].convertTo(images[i], images[0].type());
        }
        result = cv::Mat(newrow, newcol, images[0].type(), cv::Scalar(255, 255, 255));

        cv::Range rangerow, rangecol;
        int start = 0;
        for (int i = 0; i < num; ++i) {
            rangerow = cv::Range((newrow - images[i].rows) / 2, (newrow - images[i].rows) / 2 + images[i].rows);
            rangecol = cv::Range(start, start + images[i].cols);
            images[i].copyTo(result(rangerow, rangecol));
            start += images[i].cols;
        }
    }
        // 纵向拼接
    else if (type == 1) {
        int mincol = 10000;
        for (int i = 0; i < num; ++i) {
            if (mincol > images[i].cols)
                mincol = images[i].cols;
        }
        newcol = mincol;
        for (int i = 0; i < num; ++i) {
            int trow = images[i].rows * mincol / images[i].cols;
            int tcol = newcol;
            cv::resize(images[i], images[i], cv::Size(tcol, trow));
            newrow += images[i].rows;
            if (images[i].type() != images[0].type())
                images[i].convertTo(images[i], images[0].type());
        }
        result = cv::Mat(newrow, newcol, images[0].type(), cv::Scalar(255, 255, 255));

        cv::Range rangerow, rangecol;
        int start = 0;
        for (int i = 0; i < num; ++i) {
            rangecol = cv::Range((newcol - images[i].cols) / 2, (newcol - images[i].cols) / 2 + images[i].cols);
            rangerow = cv::Range(start, start + images[i].rows);
            images[i].copyTo(result(rangerow, rangecol));
            start += images[i].rows;
        }
    }

    return result;
}



void fun() {
    //文献[3]中在此处需要调用PyGILState_Check()检测当前线程是否拥有GIL，
    //但我的环境并不能编译PyGILState_Check，但我实测没有PyGILState_Check也OK.
    PyGILState_STATE gstate;
    gstate = PyGILState_Ensure();   //如果没有GIL，则申请获取GIL
    Py_BEGIN_ALLOW_THREADS;
        Py_BLOCK_THREADS;

        // 调用Python C API函数，如PyRun_SimpleString、PyImport_Import、PyObject_CallObject、……
        PyObject *pModule, *pFunc;

        PyRun_SimpleString("import sys\n");
        PyObject *sys_path = PySys_GetObject("path");
        PyRun_SimpleString("sys.path.append('/home/daybeha/Documents/github/DeepLabV3_ws/src/superglue/script')");
        PyRun_SimpleString("print(sys.path)");

        // 检查当前Python版本
        PyRun_SimpleString("import platform\n"
//                           "print(f\"\\033[92m Python Version: {platform.python_version()}\\033[0m\")\n");
                            "print(f\"Python Version: {platform.python_version()}\")\n");



        // 导入 python 模块
        pModule = PyImport_ImportModule(python_modle);

        if (pModule != NULL) {
            cout << "loaded " << python_modle << endl;

            // 获取目标python函数
            pFunc = PyObject_GetAttrString(pModule, python_func);

            /* pFunc is a new reference */
            if (pFunc && PyCallable_Check(pFunc)) {
                // 读取图片
                cv::Mat img0 = cv::imread("/home/daybeha/Documents/Dataset/Kitti/sequences/00/image_0/000005.png",
                                          cv::IMREAD_GRAYSCALE);
                cv::Mat img1 = cv::imread("/home/daybeha/Documents/Dataset/Kitti/sequences/00/image_0/000020.png",
                                          cv::IMREAD_GRAYSCALE);

                float score = compute_match_score(img0, img1, pFunc);
                cout << "a, score: " << score << endl;

            } else {
                if (PyErr_Occurred())
                    PyErr_Print();
                fprintf(stderr, "Cannot find function \"%s\"\n", python_func);
            }
            Py_XDECREF(pFunc);
            Py_DECREF(pModule);
        } else {
            PyErr_Print();
            fprintf(stderr, "Failed to load \"%s\"\n", python_modle);
        }

        Py_UNBLOCK_THREADS;
    Py_END_ALLOW_THREADS;
    PyGILState_Release(gstate);    //释放当前线程的GIL
}


int main() {
    //初始化Python环境，如果是C++ class，可以将这一部分放到class的构造函数中去。
    //**********************************************
    Py_Initialize(); //初始化Python环境
    if (!Py_IsInitialized()) //检测是否初始化成功
    {
        return -1;
    } else {
        PyEval_InitThreads();     //开启多线程支持
        int nInit = PyEval_ThreadsInitialized();  //检测线程支持是否开启成功
        if (nInit) {
            PyEval_SaveThread();  //因为调用PyEval_InitThreads成功后，当前线程就拥有了GIL，释放当前线程的GIL，
        }
    }

    thread *a = new thread(fun);

//    loopclose *lc = new loopclose();
//    thread *b = new thread(&loopclose::run, lc);
//
//    b->join();
//    cout << "b finished!" << endl;
    a->join();
    cout << "a finished!" << endl;


    //结束Python环境，如果是C++ class，可以将这一部分放到class的析构函数中去。
    //**********************************************
    PyGILState_Ensure();
    Py_Finalize();
    //**********************************************
    return 0;
}